from __future__ import annotations
import re
import uuid

from utils.file_util import FileUtil

from_type = "_$type"
name_map = {
    "PredefinedTypes": "Uml",
    "PredefinedTypesCpp": "UmlC++",
}


class WizConvertor:

    def __init__(self, file_path: str) -> None:
        self.json = FileUtil.read_json_file(file_path)
        self.wiz = {}

    def to_json(self) -> dict:
        self.wiz = self.__to_profile(self.json)

        return self.wiz

    def __to_profile(self, src: dict) -> dict:
        to = {}
        to["id"] = src["_id"][5:-1]
        if len(to["id"]) < 36:
            to["id"] = uuid.uuid4().__str__()
            print(f"name: {src["_name"]}, __to_profile.id: {src["_id"][5:-1]} -> {to['id']}")
        to["name"] = self.__get_sbs_string(src["_name"])
        for key, value in name_map.items():
            if to["name"] == key:
                to["name"] = value
        to["uml_type"] = self.__get_sbs_string(src[from_type])[1:]
        to["readonly"] = True
        self.__get_sbs_description(src, to)
        self.__to_declaratives(src, "ISubsystem", to, "declaratives")
        self.__to_declaratives(src, "IStereotype", to, "declaratives")
        self.__to_elements(src, to, "elements")
        self.__to_table_layouts(src, to, "elements")

        return to

    def __to_declaratives(self, src: dict, src_type: str, to_parent: dict, key: str) -> WizConvertor:
        if src.get("Declaratives") is None:
            return self

        to = []
        elements = src["Declaratives"]["value"]
        for element in elements:
            if element[from_type] == "ISubsystem":
                if element[from_type] == src_type:
                    to.append(self.__to_profile(element))
            elif element[from_type] == "IStereotype":
                if element[from_type] == src_type:
                    to.append(self.__to_stereotype(element))
            elif element[from_type] == "IDiagram":
                # skip
                pass
            else:
                print(f"__to_subsystems.Declaratives: {element[from_type]} 未处理")

        if len(to) > 0:
            to_elements = to_parent.get(key)
            if to_elements is None:
                to_elements = []
                to_parent[key] = to_elements
            for element in to:
                to_elements.append({element["uml_type"]: element})

        return self

    def __to_stereotype(self, src: dict) -> dict:
        to = {}
        to["id"] = src["_id"][5:-1]
        to["name"] = self.__get_sbs_string(src["_name"])
        if to["name"].startswith("CORBA"):
            to["name"] = to["name"].replace("CORBA", "")
        if to["name"].startswith("OM"):
            to["name"] = to["name"].replace("OM", "").lower()
        to["uml_type"] = self.__get_sbs_string(src[from_type])[1:]
        to["readonly"] = True
        self.__to_stereotype_res(src, to)
        self.__get_sbs_description(src, to)
        self.__to_applies(src, to, "applies_to")
        self.__to_tags(src, to, "tag_defs")
        self.__to_tags(src, to, "tags")
        self.__to_inheritances(src, to, "inheritances")
        self.__to_dependencies(src, to, "dependencies")

        return to

    def __to_stereotype_res(self, src: dict, to: dict):
        properties = src.get("_properties")
        if properties is None:
            return
        subjects = properties.get("Subjects")
        if subjects is None:
            print("__to_stereotype_res.Subjects: 未处理")
            return
        sub_values = subjects.get("value")
        if sub_values is None or len(sub_values) == 0:
            print("__to_stereotype_res.Subjects.value: 未处理")
            return
        sub_value = None
        for value in sub_values:
            name = value.get("_Name")
            if name == '"Model";':
                sub_value = value
                break

        if sub_value is None:
            print(f"__to_stereotype_res.Subjects.value.Model: 未处理({to['name']})")
            return
        metaclasees = sub_value.get("Metaclasses")
        if metaclasees is None:
            print("__to_stereotype_res.MetaClasses: 未处理")
            return
        values = metaclasees.get("value")
        if values is None or len(values) == 0:
            print("__to_stereotype_res.MetaClasses.value: 未处理")
            return
        meta_value = values[0]
        values = meta_value.get("Properties").get("value")

        for value in values:
            name = value.get("_Name")
            res_value = self.__get_sbs_string(value.get("_Value"))
            if name is None:
                print("__to_stereotype_res.MetaClasses.value.Name: 未处理")
                continue
            if name == '"BrowserIcon";':
                if res_value.startswith("$OMROOT/PredefinedPictures/"):
                    res_value = res_value.replace("$OMROOT/PredefinedPictures/", "../icons/")
                elif res_value.startswith("$OMROOT/Profiles/SysML/icons/"):
                    res_value = res_value.replace("$OMROOT/Profiles/SysML/icons/", "../icons/")
                to["res_icon"] = res_value
            elif name == '"Name";':
                to["res_name"] = res_value
            elif name == '"PluralName";':
                to["res_plural_name"] = res_value

    def __to_elements(self, src: dict, to_parent: dict, key: str) -> WizConvertor:
        if src.get("Types") is None:
            return self

        to = []
        elements = src["Types"]["value"]
        for element in elements:
            if element[from_type] == "IType":
                to.append(self.__to_type(element))
            else:
                print(f"__to_types.Types: {element[from_type]} 未处理")
        if len(to) > 0:
            to_elements = to_parent.get(key)
            if to_elements is None:
                to_elements = []
                to_parent[key] = to_elements
            for element in to:
                to_elements.append({element["uml_type"]: element})

        return self

    def __to_type(self, src: dict) -> dict:
        to = {}
        to["id"] = src["_id"][5:-1]
        to["name"] = self.__get_sbs_string(src["_name"])
        if to["name"].startswith("Rhp"):
            to["name"] = to["name"].replace("Rhp", "Uml")
        if to["name"].startswith("OM"):
            to["name"] = to["name"].replace("OM", "").lower()
        to["uml_type"] = self.__get_sbs_string(src[from_type])[1:]
        to["readonly"] = True
        if src.get("_declaration") is not None:
            to["declaration"] = self.__get_sbs_string(src["_declaration"])
        to["kind"] = self.__get_sbs_string(src["_kind"])
        self.__to_ref_stereotypes(src, to, "stereotypes")
        self.__to_tags(src, to, "tags")
        self.__to_inheritances(src, to, "inheritances")
        self.__to_dependencies(src, to, "dependencies")
        self.__to_enumerations(src, to, "literals")
        self.__to_attributes(src, to, "attributes")

        return to

    def __to_attributes(self, src: dict, to_parent: dict, key: str) -> WizConvertor:
        if src.get("Attrs") is None:
            return self

        to = []
        elements = src["Attrs"]["value"]
        for element in elements:
            if element[from_type] == "IAttribute":
                to.append(self.__to_attribute(element))
            else:
                print(f"__to_attributes.Attrs: {element[from_type]} 未处理")

        if len(to) > 0:
            to_parent[key] = to

        return self

    def __to_attribute(self, src: dict) -> dict:
        to = {}
        to["id"] = src["_id"][5:-1]
        to["name"] = self.__get_sbs_string(src["_name"])
        to["uml_type"] = "Attribute"
        to["readonly"] = True
        to["data_type"] = self.__from_handle_to_element_ref(src["_typeOf"])

        return to

    def __to_enumerations(self, src: dict, to_parent: dict, key: str) -> WizConvertor:
        if src.get("Literals") is None:
            return self

        to = []
        elements = src["Literals"]["value"]
        for element in elements:
            if element[from_type] == "IEnumerationLiteral":
                to.append(self.__to_enumeration(element))
            else:
                print(f"__to_enumerations.Literals: {element[from_type]} 未处理")

        if len(to) > 0:
            to_parent[key] = to

        return self

    def __to_enumeration(self, src: dict) -> dict:
        to = {}
        to["id"] = src["_id"][5:-1]
        to["name"] = self.__get_sbs_string(src["_name"])
        to["uml_type"] = "EnumerationLiteral"
        to["readonly"] = True
        to["value"] = self.__get_sbs_string(src["_value"])

        return to

    def __to_ref_stereotypes(self, src: dict, to_parent: dict, key: str) -> WizConvertor:
        if src.get("Stereotypes") is None:
            return self

        to = []
        elements = src["Stereotypes"]["value"]
        for element in elements:
            if element[from_type] == "IHandle":
                to.append(self.__from_handle_to_element_ref(element))
            else:
                print(f"__to_stereotypes_ref.Stereotypes: {element[from_type]} 未处理")

        if len(to) > 0:
            to_parent[key] = to

        return self

    def __from_handle_to_element_ref(self, src: dict) -> dict:
        to = {}
        to["id"] = src["_id"][5:-1]
        to["name"] = self.__get_sbs_string(src["_name"]) if src.get("_name") is not None else ""
        if to["name"].startswith("Rhp"):
            to["name"] = to["name"].replace("Rhp", "Uml")
        to["uml_type"] = "ElementRef"
        to["readonly"] = True
        to["ref_id"] = src["_id"][5:-1]
        to["ref_uml_type"] = self.__get_sbs_string(src["_m2Class"])[1:]

        if src.get("_filename") is not None:
            to["ref_filename"] = self.__get_sbs_string(src["_filename"])
            if to["ref_filename"] == "PredefinedTypes.sbs":
                to["ref_filename"] = "Uml.json"
            to["ref_subsystem"] = self.__get_sbs_string(src["_subsystem"])
            if to["ref_subsystem"] == "PredefinedTypes":
                to["ref_subsystem"] = "Uml"

        return to

    def __to_tags(self, src: dict, to_parent: dict, key: str) -> WizConvertor:
        if src.get("Tags") is None:
            return self

        is_def = key == "tag_defs"

        to = []
        elements = src["Tags"]["value"]
        for element in elements:
            if element[from_type] == "ITag":
                if is_def:
                    # 假设 Tag 定义没有_base 属性
                    if element.get("_base") is not None:
                        continue
                elif element.get("_base") is None:
                    # 假设 Tag 值有_base 属性
                    continue
                if element.get("_typeOf") is not None:
                    to.append(self.__to_tag(element, is_def))
            else:
                print(f"__to_tags.Tags: {element[from_type]} 未处理")

        if len(to) > 0:
            to_parent[key] = to

        return self

    def __to_tag(self, src: dict, is_def: bool) -> dict:
        to = {}
        to["id"] = src["_id"][5:-1]
        to["name"] = self.__get_sbs_string(src["_name"])
        to["uml_type"] = "Tag" if not is_def else "TagDef"
        to["readonly"] = True
        to["type_of"] = self.__from_handle_to_element_ref(src["_typeOf"])
        # TODO: myTypeOf 没有支持
        values = src.get("ValueSpecifications")
        if values is None:
            return to
        if len(values["value"]) > 1:
            print("__to_tag.ValueSpecifications: 值数量大于1")
        for value in values["value"]:
            if value[from_type] == "ILiteralSpecification":
                to["value"] = self.__to_literal_specification(value)
            elif value[from_type] == "IInstanceValue":
                to["value"] = self.__to_literal_value(value)
            else:
                print(f"__to_tag.ValueSpecifications: {value[from_type]} 未处理")

        # 获取值的基本类型
        if not is_def:
            to["base_tag_def"] = self.__from_handle_to_element_ref(src["_base"])

        return to

    def __to_literal_specification(self, src: dict) -> dict:
        to = {}
        to["id"] = src["_id"][5:-1]
        to["name"] = ""
        to["uml_type"] = "LiteralSpecification"
        to["readonly"] = True
        to["value"] = self.__get_sbs_string(src["_value"])

        return {"LiteralSpecification": to}

    def __to_literal_value(self, src: dict) -> dict:
        to = {}
        to["id"] = src["_id"][5:-1]
        to["name"] = ""
        to["uml_type"] = "LiteralValue"
        to["readonly"] = True
        to["value"] = self.__from_handle_to_element_ref(src["_value"])

        return {"LiteralValue": to}

    def __to_inheritances(self, src: dict, to_parent: dict, key: str) -> WizConvertor:
        if src.get("Inheritances") is None:
            return self

        to = []
        elements = src["Inheritances"]["value"]
        for element in elements:
            if element[from_type] == "IGeneralization":
                to.append(self.__to_inheritance(element))
            else:
                print(f"__to_inheritances.Tags: {element[from_type]} 未处理")

        if len(to) > 0:
            to_parent[key] = to

        return self

    def __to_inheritance(self, src: dict) -> dict:
        to = {}
        to["id"] = src["_id"][5:-1]
        to["name"] = ""
        to["uml_type"] = "Generalization"
        to["readonly"] = True
        to["visibility"] = "Public"
        to["depends_on"] = self.__from_handle_to_element_ref(src["_dependsOn"])

        return to

    def __to_dependencies(self, src: dict, to_parent: dict, key: str) -> WizConvertor:
        if src.get("Dependencies") is None:
            return self

        to = []
        elements = src["Dependencies"]["value"]
        for element in elements:
            if element[from_type] == "IDependency":
                to.append(self.__to_dependency(element))
            else:
                print(f"__to_tags.Tags: {element[from_type]} 未处理")

        if len(to) > 0:
            to_parent[key] = to

        return self

    def __to_dependency(self, src: dict) -> dict:
        to = {}
        to["id"] = src["_id"][5:-1]
        to["name"] = self.__get_sbs_string(src["_name"])
        to["uml_type"] = "Tag"
        to["readonly"] = True
        self.__to_ref_stereotypes(src, to, "stereotypes")
        to["depends_on"] = self.__from_handle_to_element_ref(src["_dependsOn"])

        return to

    def __to_table_layouts(self, src: dict, to_parent: dict, key: str) -> WizConvertor:
        if src.get("TableLayouts") is None:
            return self

        to = []
        elements = src["TableLayouts"]["value"]
        for element in elements:
            if element[from_type] == "ITableLayout":
                to.append(self.__to_table_layout(element))
            else:
                print(f"__to_table_layouts.TableLayouts: {element[from_type]} 未处理")

        if len(to) > 0:
            to_elements = to_parent.get(key)
            if to_elements is None:
                to_elements = []
                to_parent[key] = to_elements
            for element in to:
                to_elements.append({element["uml_type"]: element})

        return self

    def __to_table_layout(self, src: dict) -> dict:
        to = {}
        to["id"] = src["_id"][5:-1]
        to["name"] = self.__get_sbs_string(src["_name"])
        to["uml_type"] = "TableLayout"
        to["readonly"] = True
        self.__to_table_element_types(src, to, "table_element_types")
        self.__to_data_columns(src, to, "data_columns")
        return to

    def __to_table_element_types(self, src: dict, to_parent: dict, key: str) -> WizConvertor:
        if src.get("TableElementTypes") is None:
            return self

        to = []
        elements = src["TableElementTypes"]["value"]
        for element in elements:
            if element[from_type] == "TableDataDefinition":
                to.append(self.__to_table_element_type(element))
            else:
                print(f"__to_table_layouts.TableLayouts: {element[from_type]} 未处理")

        if len(to) > 0:
            to_parent[key] = to

        return self

    def __to_table_element_type(self, src: dict) -> dict:
        to = {}
        to["name"] = self.__get_sbs_string(src["_name"])
        to["data_type"] = src["_DataType"]

        return to

    def __to_data_columns(self, src: dict, to_parent: dict, key: str) -> WizConvertor:
        if src.get("DataColumns") is None:
            return self

        to = []
        elements = src["DataColumns"]["value"]
        for element in elements:
            if element[from_type] == "TableDataDefinition":
                to.append(self.__to_data_column(element))
            else:
                print(f"__to_table_layouts.TableLayouts: {element[from_type]} 未处理")

        if len(to) > 0:
            to_parent[key] = to

        return self

    def __to_data_column(self, src: dict) -> dict:
        to = {}
        to["name"] = self.__get_sbs_string(src["_name"])
        if src.get("_columnName") is not None:
            to["column_name"] = self.__get_sbs_string(src["_columnName"])

        return to

    def __to_applies(self, src: dict, to_parent: dict, key: str) -> WizConvertor:
        if src.get("_m2Classes") is None or src.get("_m2Classes").get("value") is None:
            return self

        text = src["_m2Classes"]["value"][1:-2]
        to = text.split('"; "')

        # for to_ele in to:
        #     print(f"applies object: {to_ele}")

        if len(to) > 0:
            to_parent[key] = to

        return self

    def __get_sbs_string(self, src: str) -> str:
        if len(src) == 0:
            return ""

        to = src
        # 去除末尾的分号
        if to[-1] == ';':
            to = to[:-1]

        # 去掉双引号
        if to[0] == '"':
            to = to[1:-1]

        # print(f"__get_sbs_string: {src} -> {to}")
        return to

    def __get_sbs_description(self, src: str, to: dict) -> WizConvertor:
        des = src.get("_description")
        if des is None:
            return self

        rtf = des.get("_textRTF")
        if rtf is None:
            return self

        rtf = self.parse_rtf_remove(r"\\[0-9a-zA-Z]*", self.__get_sbs_string(rtf.strip()))

        new_rtf = self.parse_rtf_remove(r"\{[ *a-zA-Z();]*;\}", rtf)
        while new_rtf != rtf:
            rtf = new_rtf
            new_rtf = self.parse_rtf_remove(r"\{[ *a-zA-Z();]*;\}", rtf)

        rtf = self.parse_rtf_remove(r"[{}]", rtf)
        rtf = re.sub(r"\s+", " ", rtf)
        rtf = re.sub(r"\s+,", ",", rtf)
        rtf = self.__get_sbs_string(rtf.strip())
        to["description"] = rtf.strip()

        return self

    def parse_rtf_remove(self, pattern, content: str) -> str:
        return re.sub(pattern, "", content)
